#include <stdlib.h>
#include <stdio.h>

#include "../../libs/Utils/Utils.h"
#include "../../libs/LinearComputation/Vector.h"
#include "../../libs/SparceMatrix/sparse_matrix.h"
#include "../../libs/SparceMatrix/LinearIO.h"
#include "../../libs/ImproveDiv/ScoreFunctions.h"
#include "../../libs/LinearComputation/ModularityMatrix.h"
#include "../../libs/Containers/NumList.h"
#include "../../libs/Containers/NumListIO.h"
#include "../../libs/Containers/ListOfNumList.h"
#include "../../libs/Containers/Utils.h"
#include "../../libs/ImproveDiv/ScoreFunctions.h"

int main(int argv, char* argc[])
{
	sparse_matrix_lst* adjMatrix = NULL, * partialAdjMatrix = NULL;
	NumList *group = NULL, *subGroup = NULL, *iter = NULL, *iter2 = NULL;
	ListOfNumList *result = NULL;
	elem* groupVec = NULL, * degVec = NULL, *partialDegVec = NULL,
		*modularityMat = NULL, *topedModularityMat = NULL;
	elem* deltaQ, Q, Q2;
	int i = 0;
	bool ret = TRUE;

	TAssertM(argv >= 4, "Not enough arguments.");
	TAssertM(NULL != (adjMatrix = ReadInMatrix(argc[1])), "Unable to read matrix.");

	if (!(ret = !(NULL == (group = ReadNumList(argc[2]))
			|| NULL == (subGroup = ReadNumList(argc[3]))
			|| NULL == (partialAdjMatrix = PartialMatrix(adjMatrix, group)))))
		perror("failed reading data from one of the files");

	if (ret)
		if(!(ret = MemoryAssert(5, 
				groupVec = (elem*)malloc(sizeof(elem) * group->size),
				degVec = DegVector(adjMatrix, (elem*)malloc(sizeof(elem) * adjMatrix->n)),
				modularityMat = PartialModularityMatrix(adjMatrix, group, 
						(elem*)malloc(sizeof(elem) * group->size * group->size)),
				topedModularityMat = CreateModularityMatrix(adjMatrix, group),
				deltaQ = (elem*)malloc(sizeof(elem)))
				&& MemoryAssert(1, partialDegVec = PartialVector(degVec, group))))
			perror("coudn't allocate memory");

	if (ret)
	{
		for (iter = group, iter2 = subGroup, i = 0; iter->next != NULL; iter = iter->next)
		{
			if ((iter2->next == NULL) || (iter2->value < iter->value))
			{
				perror("secondGroup is not a sub-group of the first on");
				ret = FALSE;
				break;
			}
			
			if (iter->value == iter2->value)
			{
				groupVec[i++] = -1;
				iter2 = iter2->next;
			}
			else
				groupVec[i++] = 1;
		}
	}

	if (ret)
	{	
		*deltaQ = 0;
		
		Q = ScalarProduct(MultVectorByMatrix(topedModularityMat, groupVec, partialAdjMatrix->n), groupVec, partialAdjMatrix->n);
		ImproveDiv(groupVec, partialAdjMatrix, partialDegVec, 
				1.0 / SumOfVector(degVec, adjMatrix->n), modularityMat, deltaQ);
		
		Q2 = ScalarProduct(MultVectorByMatrix(topedModularityMat, groupVec, partialAdjMatrix->n), groupVec, partialAdjMatrix->n);
		
		if ((result = SplitNumList(group, groupVec)) == NULL)
				ret = FALSE;

			if(ret)
			{
				if (result->size != 2)
					ret = FALSE;
				else
				{
					ret = printf("%lf\n", *deltaQ) >= 0;
					if (ret && result->value->next != NULL)
						ret = PrintNumList(result->value);
					if (ret && result->next->value->next != NULL)
						ret = printf("\n") 
							&& PrintNumList(result->next->value);
				}
			}
	}
		
	free_sparse_matrix_lst(partialAdjMatrix);
	free_sparse_matrix_lst(adjMatrix);
	DeleteNumList(group);
	DeleteNumList(subGroup);
	DeleteListOfNumList(result, TRUE);
	MemoryAssert(6, NULL, groupVec, degVec, partialDegVec, modularityMat, topedModularityMat);
	
	return !ret;
}
